//go:build linux
// +build linux

package main

import (
	"fmt"
	"net"
	"sync"
	"syscall"
)

type EPoll struct {
	look            sync.RWMutex
	conn            map[int]*ServerConn
	socketFd        int
	connCallback    ConnectedEvent
	receiveCallback ReceiveEvent
}

var epollFd int

type ServerConn struct {
	fd   int
	Addr string
	Port int
}

type ConnectedEvent func(connected bool, cli *ServerConn) // tcp连接与断开事件

type ReceiveEvent func(cli *ServerConn) // tcp连接,有可接收数据事件

var epoll *EPoll

func NewEpoll() *EPoll {
	epoll = &EPoll{
		conn: make(map[int]*ServerConn),
	}
	return epoll
}

// Close 关闭服务
func (c *EPoll) Close() (e error) {
	// 首先关闭所有连接的客端
	for _, con := range c.conn {
		_ = c.CloseConn(con.fd)
	}
	if e = syscall.Close(epollFd); e != nil {
		return
	}
	if e = syscall.Close(c.socketFd); e != nil {
		return
	}
	return
}

// SetConnectEvent 设置连接事件
func (c *EPoll) SetConnectEvent(f ConnectedEvent) {
	c.connCallback = f
}

// SetReceiveEvent 设备收接数据事件
func (c *EPoll) SetReceiveEvent(f ReceiveEvent) {
	c.receiveCallback = f
}

// GetConn 获取连接
// fd：连接句柄
func (c *EPoll) GetConn(fd int) *ServerConn {
	c.look.RLock()
	defer c.look.RUnlock()
	return c.conn[fd]
}

// AddConn 添加新的客户端连接到连接列表
func (c *EPoll) AddConn(conn *ServerConn) {
	c.look.Lock()
	c.conn[conn.fd] = conn
	c.look.Unlock()
}

func (c *EPoll) DelConn(fd int) {
	c.look.Lock()
	c.conn[fd] = nil
	delete(c.conn, fd)
	c.look.Unlock()
}

// Listen 开始临听
func (c *EPoll) Listen(ipAddr string, port int) error {
	fd, e := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
	if e != nil {
		return e
	}
	var addr [4]byte
	copy(addr[:], net.ParseIP(ipAddr).To4())
	e = syscall.Bind(fd, &syscall.SockaddrInet4{
		Port: port,
		Addr: addr,
	})
	if e != nil {
		return e
	}

	if e = syscall.Listen(fd, 10); e != nil {
		return e
	}
	c.socketFd = fd
	e = c.CreateEpoll()
	return e
}

// EpollAddEvent 向epoll中加事件
func (c *EPoll) EpollAddEvent(fd int) error {
	return syscall.EpollCtl(epollFd, syscall.EPOLL_CTL_ADD, fd, &syscall.EpollEvent{
		Events: syscall.EPOLLIN | syscall.EPOLLHUP | syscall.EPOLLERR,
		Fd:     int32(fd),
		Pad:    0,
	})

}

// EpollRemoveEvent 从epoll中删除事件
func (c *EPoll) EpollRemoveEvent(fd int) error {
	return syscall.EpollCtl(epollFd, syscall.EPOLL_CTL_DEL, fd, nil)
}

func (c *EPoll) GetConnectSum() int {
	return len(c.conn)
}

func (c *EPoll) Accept() error {
	for {
		nfd, sa, e := syscall.Accept(c.socketFd)
		if e != nil {
			return e
		}

		e = c.EpollAddEvent(nfd) // 客户端socket注册临听事件
		if e != nil {
			return e
		}

		v := sa.(*syscall.SockaddrInet4)
		ip := fmt.Sprintf("%d.%d.%d.%d", v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3])
		cli := &ServerConn{
			fd:   nfd,
			Port: v.Port,
			Addr: ip,
		}
		c.AddConn(cli) // 添加到客户端列表
		if c.connCallback != nil {
			go c.connCallback(true, cli) // 发送连接事件
		}
	}
}

// HandlerEpoll 处理epoll（监听socket事件）
func (c *EPoll) HandlerEpoll() error {
	events := make([]syscall.EpollEvent, 1)
	//在死循环中处理epoll
	for {
		//msec -1,会一直阻塞,直到有事件可以处理才会返回, n:事件个数
		n, _ := syscall.EpollWait(epollFd, events, -1)
		//fmt.Println("收到事件：", n, events[0].Events)
		for i := 0; i < n; i++ {
			//先在map中是否有这个链接
			conn := c.GetConn(int(events[i].Fd))
			if conn == nil { //没有这个链接,忽略
				continue
			}
			event := events[i].Events
			// 删除客户端的事件注册（原因：在断开连接，会产生多少读取事件而且不产生断开事件，
			// 这可能与linux版本有关系，发送时也是一个字符一次事件，容易产生混乱）
			_ = c.EpollRemoveEvent(conn.fd)
			go func() {
				_ = c.HandlerEpollEvent(conn, event)
			}()
		}
	}
}

// HandlerEpollEvent 处理socket事件
func (c *EPoll) HandlerEpollEvent(conn *ServerConn, event uint32) error {
	con := c.GetConn(conn.fd)
	if con == nil { // 没有这个链接,忽略
		return nil
	}
	if event&syscall.EPOLLHUP == syscall.EPOLLHUP || event&syscall.EPOLLERR == syscall.EPOLLERR {
		// 断开||出错
		fmt.Println("断开")
		if err := c.CloseConn(conn.fd); err != nil {
			return err
		}
	} else if event == syscall.EPOLLIN {
		// 可读事件
		defer func() {
			_ = c.EpollAddEvent(conn.fd)
		}()

		if c.receiveCallback != nil && con != nil {
			c.receiveCallback(conn)
		}
	}
	return nil
}

// CloseConn 关闭指定的链接
func (c *EPoll) CloseConn(fd int) error {
	conn := c.GetConn(fd)
	if conn == nil {
		return nil
	}
	_ = c.EpollRemoveEvent(fd)
	_ = conn.Close()
	c.DelConn(fd)
	if c.connCallback != nil {
		go c.connCallback(false, conn)
	}
	return nil
}

// CreateEpoll 创建一个epoll
func (c *EPoll) CreateEpoll() error {
	//通过系统调用,创建一个epoll
	fd, err := syscall.EpollCreate(1)
	if err != nil {
		return err
	}
	epollFd = fd
	return nil
}

// ===========================================================

// Close 关闭这个链接
func (s *ServerConn) Close() error {
	e := epoll.EpollRemoveEvent(s.fd)
	epoll.DelConn(s.fd)
	e = syscall.Close(s.fd)
	if epoll.connCallback != nil {
		go epoll.connCallback(false, s)
	}
	return e
}

// 读取数据（接收到可读取事件时，第一次进行读取，返回读取长度为0时,可认为是连接断开）
func (s *ServerConn) Read(data []byte) (int, error) {
	//通过系统调用,读取数据,n是读到的长度
	n, e := syscall.Read(s.fd, data)
	if e != nil {
		e = s.Close()
		return 0, e
	}
	if n <= 0 {
		e = s.Close()
		return 0, e
	}

	return n, e
}

// 向这个链接中写数据
func (s *ServerConn) Write(data []byte) (int, error) {
	n, err := syscall.Write(s.fd, data)
	return n, err
}
